﻿// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

using Longbow.Tasks.EntityFrameworkCore;
using Longbow.Tasks.EntityFrameworkCore.Model;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Longbow.Tasks.EntityFrameworkCoreStorage
{
    /// <summary>
    /// 
    /// </summary>
    public class EFCoreStorage : IStorage
    {
        private SchedulerDBContext Context { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public Exception? Exception { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public ILogger Logger { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <param name="logger"></param>
        public EFCoreStorage(SchedulerDBContext context, ILogger logger)
        {
            Context = context;
            Logger = logger;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public virtual async Task LoadAsync()
        {
            try
            {
                var schedulers = await Context.Schedulers.AsNoTracking().ToListAsync();
                if (schedulers != null)
                {
                    foreach (var item in schedulers)
                    {
                        var triggerType = typeof(IStorage).Assembly.GetType(item.TriggerType);
                        if (triggerType != null)
                        {
                            if (Activator.CreateInstance(triggerType) is ITrigger trigger)
                            {
                                var properties = item.TriggerProperties.ToDictionary(Logger);
                                if (properties != null)
                                {
                                    trigger.LoadData(properties);
                                    var task = CreateTaskByScheduleName(item.SchedulerName);
                                    if (task != null)
                                    {
                                        TaskServicesManager.GetOrAdd(item.SchedulerName, task, trigger);
                                    }
                                    else
                                    {
                                        var callback = CreateCallbackByScheduleName(item.SchedulerName);
                                        if (callback != null)
                                        {
                                            TaskServicesManager.GetOrAdd(item.SchedulerName, callback, trigger);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Exception = ex;
                Logger.LogError(ex, "加载任务失败");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="scheduleName"></param>
        /// <returns></returns>
        protected virtual ITask? CreateTaskByScheduleName(string scheduleName) => null;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="scheduleName"></param>
        /// <returns></returns>
        protected virtual Func<CancellationToken, Task>? CreateCallbackByScheduleName(string scheduleName) => null;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="schedulerName"></param>
        /// <param name="trigger"></param>
        /// <returns></returns>
        public bool Save(string schedulerName, ITrigger trigger)
        {
            var model = new Scheduler()
            {
                SchedulerName = schedulerName,
                TriggerProperties = trigger.SetData().ToJosnString(),
                TriggerType = trigger.GetType().FullName,
            };
            Context.Schedulers?.Add(model);
            return Context.SaveChanges() > 0;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="schedulerNames"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public bool Remove(IEnumerable<string> schedulerNames)
        {
            var entities = Context.Schedulers?.FirstOrDefault(s => schedulerNames.Contains(s.SchedulerName));
            Context.RemoveRange(entities);
            return Context.SaveChanges() > 0;
        }
    }
}
